﻿using System;
using System.Data.Entity;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using InseSoft.Infraestructure;
using InseSoft.Models;
using InseSoft.Models.Entities;
using InseSoft.Services;
using InseSoft.ViewModels.Events;
using Ninject;

namespace InseSoft.ViewModels
{
    public class EntityViewModel<T> : ObservableObject, IEntityViewModel where T : class
    {
        #region Declarations

        protected const string ConfirmDeleteCaption = "Confirmar borrado";
        protected const string ConfirmDeleteMessage = "¿Está seguro de borrar este item?";
        private Entity currentEntity;

        #endregion

        #region Properties

        [Inject]
        public IEventPublisher EventAggregator { get; set; }

        [Inject]
        public virtual IInseContext DbContext { get; set; }

        [Inject]
        public IDialogService DialogService { get; set; }

        public Entity CurrentEntity
        {
            get { return currentEntity; }
            set
            {
                currentEntity = value;
                OnPropertyChanged(() => CurrentEntity);
                OnPropertyChanged(() => IsItemSelected);
            }
        }

        protected IDbSet<T> Entities
        {
            get { return DbContext.Set<T>(); }
        }

        public bool IsItemSelected
        {
            get { return CurrentEntity != null; }
        }

        #endregion

        #region Commands

        public ICommand NewCommand
        {
            get { return new RelayCommand(NewExecute, NewCanExecute); }
        }

        public ICommand DeleteCommand
        {
            get { return new RelayCommand(DeleteExecute, () => IsItemSelected); }
        }

        public ICommand SaveCommand
        {
            get { return new RelayCommand(SaveExecute, SaveCanExecute); }
        }

        public ICommand ExitCommand
        {
            get { return new RelayCommand(ExitExecute); }
        }

        #endregion

        #region Methods

        public virtual void Initialize()
        {
            EventAggregator.GetEvent<EntitySelected>().Subscribe(SelectItem);
        }

        protected virtual bool NewCanExecute()
        {
            return true;
        }

        private bool SaveCanExecute()
        {
            if (DbContext != null)
            {
                var entities = DbContext.AddedOrModifiedEntities<T>();
                var areAllValid = entities.All(entity => entity.IsValid);
                return DbContext.HasPendingChanges() && areAllValid;    
            }
            return false;
        }

        private void SelectItem(EntitySelected newSelection)
        {
            CurrentEntity = newSelection.Item;
        }
        
        private void DeleteExecute()
        {
            if (DialogService.ShowMessage(ConfirmDeleteMessage, ConfirmDeleteCaption, DialogButton.OkCancel, DialogImage.Question) != DialogResponse.Ok) return;

            Entities.Remove(currentEntity as T);
            DbContext.Save();
            EventAggregator.Publish(new EntityRemoved(CurrentEntity));
            ResetForm();
        }

        private void SaveExecute()
        {
            DbContext.Save();
        }

        protected virtual void NewExecute()
        {
            ResetForm();
            var newItem = EntityFactory.CreateCatalogEntity<T>();
            Entities.Add(newItem);
            CurrentEntity = newItem as Entity;
            if (CurrentEntity == null) return;
            CurrentEntity.ConfigureValidationRules();
            EventAggregator.Publish(new EntityAdded(CurrentEntity));
        }

        private void ExitExecute()
        {
            DbContext.Dispose();
            var window = Application.Current.Windows.Cast<Window>().LastOrDefault(w => w.IsActive);
            if(window != null) window.Close();
        }

        private void ResetForm()
        {
            CurrentEntity = null;
        }

        public void Dispose()
        {
            DbContext.Dispose();
            DbContext = null;
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
